/*
* Unless required by applicable law or agreed to in writing, software 
* distributed under the License is distributed on an "AS IS" BASIS, 
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
* See the License for the specific language governing permissions and 
* limitations under the License. 
*/
#ifndef CMDLINE_H_
#define CMDLINE_H_
#include <map>
#include <vector>

#include "cmdArg.h"
#include "cmdOption.h"


typedef  std::map<char,AbstractOption*>::iterator OPT_ITERATOR;  
typedef  std::vector<AbstractArg*>::iterator ARG_ITERATOR;

class cmdLine
{
public:
	
	const static char* HELP_KEY;
	cmdLine(const char* usage);
	virtual ~cmdLine();
	template<class T> 
		void AddArg(const char* parserStr, const char* desc, bool optional = true)
	{
		addArg(new cmdArg<T>(parserStr, desc, optional));
	}

	template<class T> 
		void AddArg(const char* parserStr, const char* desc, T defaultValue)
	{
		cmdArg<T> *arg = new cmdArg<T>(parserStr, desc, false);
		*arg = defaultValue;
		addArg(arg);		
	}

	template<class T, class P> 
		void AddArg(const char* desc, bool optional = true)
	{
		addArg(new cmdArg<T,P>(desc, optional));
	}

	template<class T, class P> 
		void AddArg(const char* desc, T defaultValue)
	{
		cmdArg<T,P> *arg = new cmdArg<T,P>(desc, false);
		*arg = defaultValue;
		addArg(arg);		
	}


	template<class T> 
		void AddOptionArg(char k, const char* desc, const char* parserStr)
	{
		cmdArg<T> *arg = new cmdArg<T>(parserStr, desc, false); // all options are "optional"
		cmdOption<T> *opt = new cmdOption<T>(k, desc, arg);
		addOption(opt);
	}

	template<class T, class P> 
		void AddOptionArg(char k, const char* desc)
	{
		cmdArg<T,P> *arg = new cmdArg<T,P>(NULL, false); // all options are "not optional"
		addOption(new cmdOption<T>(k, desc, arg));
		
	}

	template<class T> 
		void AddOptionArg(char k, const char* verboseKey, const char* desc, const char* parserStr)
	{
		cmdArg<T> *arg = new cmdArg<T>(parserStr, NULL, false); // all options are "not optional"
		addOption(new cmdOption<T>(k, verboseKey, desc, arg));
		
	}

	template<class T, class P> 
		void AddOptionArg(char k, const char* verboseKey, const char* desc)
	{
		cmdArg<T,P> *arg = new cmdArg<T,P>(desc, false); // all options are "not optional"
		addOption(new cmdOption<T>(k, verboseKey, desc, arg));
		
	}


	
	void AddOption(char k, const char* desc);
	void AddOption(char k, const char* verboseKey, const char* desc);
	void AddStrArg(const char* desc = NULL, bool optional = true);
	void AddStrOption(char key, const char* verboseKey = NULL, const char* desc = NULL);
	
	bool IsKeySet(char key);
	bool IsKeySet(char* veboseKey);	
	bool HasOption(char key);
	bool HasOption(char* veboseKey);
	bool HasOption(AbstractOption& opt);
	
	
	
	int OptCount();
	int ArgCount();
	
	char* OptionKeyList(); 
	
	AbstractOption* GetOption(char key);
	template<class T> T Value(char key)
	{
		if (HasOption(key)) { 
			
			cmdArg<T>* p = (cmdArg<T>*)options[key]->GetArg();
			return p->Value(); 
		}
		
		throw (ERR_NOTDEFINED);	
	}


	template<class T> T Value(char* veboseKey)
	{
		return Value<T>(verbose2key(veboseKey));
	}

	template<class T> T Value(int n)
	{
		if (n < 0 || n > ArgCount())
			throw (ERR(ERR_OUT_OF_RANGE));
		cmdArg<T>* p = (cmdArg<T>*)args[n];
		
		return p->Value(); 
	}

	AbstractOption& operator[](char key)
	{
		AbstractOption* p = GetOption(key);
		return 	*p;
	}

	AbstractOption& operator[](char* veboseKey)
	{
		AbstractOption* p = GetOption(verbose2key(veboseKey));
		return 	*p;
	}
	
	AbstractArg& operator[](int n)
	{
		AbstractArg* p = GetArg(n); 
		return *p;
	}

	
	void Clear();
	AbstractArg* GetArg(int n);
	const char* AppName() {return appName;}
	void PrintHelpMsg(std::ostream& out = std::cout);
	// parse cmd line
	ErrCode_t Parse(int argc, char **argv);
private:
	void addOption(AbstractOption* opt);
	void addArg(AbstractArg* argument);
	std::map<char, AbstractOption*> options;
	std::vector<AbstractArg*> args;
	const char* appName;
	const char* usageDesc;
	char verbose2key(const char* veboseKey);
};

#endif /*CMDLINE_H_*/
